import astar
import game
import map
from pyglet.gl import *
import sprite
import thing
import vector
import weapon


class Unit(thing.Thing):

	group = sprite.Group()

	def __init__(self, id, owner, health, location, target, destination):
		thing.Thing.__init__(self, id, owner, health, location, self.size, target)
		game.state.push_handlers(self)
		self.apparent_location = vector.Vec3(*location)
		self.destination = destination
		self.path = None

		self.vertex_list = pyglet.graphics.vertex_list(9, 'v3f', 'c3f',
			('n3f/static', (
				0, 1, 0.5, 0, 1, 0.5, 0, 1, 0.5,
				-0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
				0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5,
			)))
		# ???: If a unit is destroyed do we need to delete the vertex list?

	def update_vertices(self):
		x, y, z = game.state.tactical_handler.map_view.opengl_coordinates(self.location)
		hs = self.size / 2.0
		self.vertex_list.vertices = (
			x, y, z + self.size, x + hs, y + hs, z, x - hs, y + hs, z,
			x, y, z + self.size, x - hs, y + hs, z, x, y - hs, z,
			x, y, z + self.size, x, y - hs, z, x + hs, y + hs, z,
			)

	def on_draw_tactical_iso(self):
		self.update_vertices()
		self.update_color()
		self.vertex_list.draw(GL_TRIANGLES)

		thing.Thing.draw(self)

	def on_unit_state_update(self, unit_id, owner, health, location, size, target, destination):
		if self.id == unit_id:
			self.on_unit_move(unit_id, destination)
			thing.Thing.on_thing_state_update(self, owner, health, location, size, target)

	def on_unit_move(self, unit_id, destination):
		if self.id == unit_id and self.destination != destination:
			self.path = None
			self.destination = destination

	def on_update(self, dt):
		movement_goals = []

		# collisions
		avoiding = []
		for a in thing.Thing.group:
			if a is not self:
				offset = self.location - a.location
				distance = offset.length()
				if distance != 0 and distance < (0.6 * (a.size + self.size)):
					repulse = 1.0 / ((distance - (0.6 * a.size)) ** 2)
					avoiding.append(offset.normalize() * repulse)
		if avoiding:
			# collision avoidance movement goal
			movement_goals.append(sum(avoiding, vector.Vec3()).normalize())

		# navigational movement
		fired_recently = False
		for weapon in self.weapons:
			if weapon.fired and game.state.time <= (weapon.fired[2] + weapon.fire_delay):
				fired_recently = True
				break
		if not fired_recently:
			# stop moving if we've fired recently
			if self.destination and self.path is None:
				# compute path to destination if we haven't already
				start = self.navigation.get(game.state.map.tile(*self.location).position)
				goal = self.navigation.get(game.state.map.tile(*self.destination).position)
				self.path = astar.Search(start, [goal]).search()
				if self.path is None: self.path = [] # don't retry finding a path when there isn't one
			if self.path:
				# destination reaching movement goal
				movement_goals.append((self.path[0].tile.location - self.location).normalize())

		# combine the movement goals into the final action
		if movement_goals:
			self.location += sum(movement_goals, vector.Vec3()) * (1.0 / len(movement_goals)) * self.speed * self.speed_multiplier * dt
			close = self.size
			if self.path and close > (self.location - self.path[0].tile.location).length():
				# reached intermediate destination
				self.path.pop(0) # move to next item in path
			if self.destination and close > (self.location - self.destination).length():
				# reached our destination
				self.destination = None
				self.path = []

		# we smooth the "apparent" coordinates so that
		# the drawn coordinates are smoothed over time
		self.apparent_location += self.location
		self.apparent_location *= 0.5

		thing.Thing.on_update(self, dt)

	def on_unit_kill(self, unit_id):
		if self.id == unit_id:
			game.state.remove_handlers(self)
			self.kill()

	@property
	def navigation(self):
		return getattr(game.state.map, self.movement_type.lower()+'_nodes')

	@property
	def speed_multiplier(self):
		tile = game.state.map.tile(*self.location)
		if tile is not None:
			return getattr(tile, self.movement_type.upper()+'_SPEED')
		return 1.0


# Sleek


class Rifleman(Unit):

	mineral_cost = 100
	gas_cost = 0
	size = 0.3
	health = 100
	speed = 0.75
	color = (0.0, 0.0, 0.5)
	weapons = [weapon.LaserRifle]
	movement_type = 'ground'


class Hoverjet(Unit):

	mineral_cost = 125
	gas_cost = 50
	size = 0.6
	health = 125
	speed = 3.0
	color = (0.0, 0.0, 0.5)
	weapons = [weapon.LaserCannon]
	movement_type = 'hover'


# Insectile


class RollyPoly(Unit):

	mineral_cost = 25
	gas_cost = 0
	size = 0.175
	health = 35
	speed = 1.5
	color = (0.5, 0.0, 0.0)
	weapons = [weapon.Bite]
	movement_type = 'hover'


# ArmoredClunky


class Bot(Unit):

	mineral_cost = 50
	gas_cost = 0
	size = 0.2
	health = 40
	speed = 1.0
	color = (0.5, 0.5, 0.0)
	weapons = [weapon.Gatling]
	movement_type = 'ground'

